/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jmodcog2.map.pix;

import jmodcog2.base.BaseRunner;

/**
 *
 * @author Mitch
 */
public abstract class FeatureMap extends BaseRunner {

    protected final int input_len_x, input_len_y;
    protected final int step_x, step_y;
    protected final int unit_len_x, unit_len_y;
    protected final int len_z;
    //
    protected final int vec_size;   //unit_len_x * unit_len_y * len_z
    protected final int num_nodes;
    protected final int num_node_dims;
    //float[] node_vecs;  //num_nodes * vec_size
    @Constant
    protected final float[] node_pos;   //num_nodes * num_node_dims
    //protected final float[] node_vec_err;
    protected final int sum_err_len;
    protected final float[] node_sum_err;
    protected final int num_units_x, num_units_y, num_units;
    //
    protected final int[] winner_index;
    // Locally _normalize input kernel 
    protected final boolean normalize;

    protected FeatureMap(FeatureMapConfig config, boolean trained) {
        super(config);
        this.input_len_x = config.dataLenX();
        this.input_len_y = config.dataLenY();
        this.step_x = config.dataStepX();
        this.step_y = config.dataStepY();
        this.unit_len_x = config.unitLenX();
        this.unit_len_y = config.unitLenY();
        this.len_z = config.dataLenZ();
        this.vec_size = unit_len_x * unit_len_y * len_z;
        this.num_nodes = config.numNodes();
        this.num_node_dims = config.numDimensions();
        this.node_pos = config.createNodePosArray();
        this.num_units_x = config.numUnitsX();
        this.num_units_y = config.numUnitsY();
        this.num_units = num_units_x * num_units_y;
        //this.node_vec_err = new float[trained? num_units * num_nodes * vec_size: num_nodes * vec_size];
        this.node_sum_err = new float[trained ? num_units * num_nodes : num_nodes];
        this.sum_err_len = node_sum_err.length;
        this.normalize = config.normalize();
        this.winner_index = new int[trained ? num_units : 1];
        this.current_unit = new float[vec_size];
        put(current_unit);
        put(node_pos);
        //put(node_vec_err);
        put(node_sum_err);
        put(winner_index);
    }
//    
//    protected void _calc_vec_err(int input_index, int node_vec_index) {
//        node_vec_err[node_vec_index] = input[input_index] - _get_node_w(node_vec_index);
//    }
//
//    protected void _calc_vec_err_norm(int input_index, int node_vec_index, float min, float max) {
//        node_vec_err[node_vec_index] = _normalize(input[input_index], min, max) - _get_node_w(node_vec_index);
//    }
    //
    @Local
    private final int[] local_win_ind = new int[64];
    @Local
    private final float[] local_win_val = new float[64];

    protected void _find_win() {
        int len = num_nodes / getLocalSize();
        int i = getGlobalId() - (getGroupId() * (num_nodes % getLocalSize()));
        i = i * len;
        int lim = i + len;
        int ind = -1;
        float val = MAX_POS;
        float v = 0;
        while (i < lim && i < sum_err_len) {
            v = node_sum_err[i];
            if (v < val) {
                val = v;
                ind = i % num_nodes;
            }
            i += 1;
        }
        int lid = getLocalId();
        local_win_ind[lid] = ind;
        local_win_val[lid] = val;
        localBarrier();
        if (lid == 0) {
            ind = -1;
            val = MAX_POS;
            i = 0;
            while (i < getLocalSize()) {
                v = local_win_val[i];
                if (v < val) {
                    ind = local_win_ind[i];
                    val = v;
                }
            }
            winner_index[_win_index(getGroupId(0), getGroupId(1))] = ind;
        }
    }
    //
    @Local
    private final float[] current_unit;

    protected void _calc_err(int unit_x, int unit_y, int node, int vec_i) {
        _init_current_unit(unit_x, unit_y, vec_i);
        _normalize_current_local(vec_i);
        _calc_local_err(node, vec_i);
        if (getLocalId() == 0) {
            float sum = 0;
            float v = 0;
            int i = 0;
            while (i < vec_size) {
                v = current_unit[i];
                sum += v * v;
            }
            sum = sum * 0.5f;
            node_sum_err[_err_index(unit_x, unit_y, node)] = sum;
        }
    }

    private void _calc_local_err(int node, int vec_i) {
        current_unit[vec_i] = current_unit[vec_i] - _get_node_w(_node_vec_index(node, vec_i));
    }

    private void _init_current_unit(int unit_x, int unit_y, int vec_i) {
        if (vec_i < vec_size) {
            current_unit[vec_i] = input[_input_vec_index(unit_x, unit_y, vec_i)];
        }
        localBarrier();
    }

    private void _normalize_current_local(int vec_i) {
        if (normalize) {
            float min = MAX_POS;
            float max = MAX_NEG;
            int i = 0;
            float v = 0;
            while (i < vec_size) {
                v = current_unit[i];
                if (v < min) {
                    min = v;
                }
                if (v > max) {
                    max = v;
                }
                i += 1;
            }
            if (min == max) {
                current_unit[vec_i] = 0.5f;
            } else {
                current_unit[vec_i] = (current_unit[vec_i] - min) / (max - min);
            }
            localBarrier();
        }
    }

    protected abstract float _get_node_w(int node_vec_index);

    protected int _input_vec_index(int unit_x, int unit_y, int vec_index) {
        int vx = _get3D_i(vec_index, unit_len_y, len_z);
        int vy = _get3D_j(vec_index, unit_len_y, len_z);
        int vz = _get3D_k(vec_index, len_z);
        //
        return _get3D_n(unit_x * step_x + vx,
                unit_y * step_y + vy, vz, input_len_y, len_z);
    }

    protected int _node_vec_index(int node, int vec_index) {
        return node * vec_size + vec_index;
    }

    protected abstract int _err_index(int unit_x, int unit_y, int node);

    protected abstract int _win_index(int unit_x, int unit_y);
//    {
//        return _get3D_n(unit_x, unit_y, node, num_units_x, num_nodes);
//    }

    protected int _output_index(int unit_x, int unit_y, int dim) {
        return _get3D_n(unit_x, unit_y, dim, num_units_y, num_node_dims);
    }
//    protected void _calc_vec_err(int input_index, int node_vec_index, int vec_err_index) {
//        node_err_vec[vec_err_index] = input[input_index] - node_vec[node_vec_index];
//    }
//    
//    protected void _calc_errSq(int vec_err_index0, int err_dsq_index) {
//        int i = vec_err_index0;
//        float sum = 0;
//        while(i < vec_err_index0 + vec_size) {
//            sum += _sq(node_err_vec[i]);
//        }
//        node_err_dSq[err_dsq_index] =  sum;
//    }
}
